package com.nakamachizu.controller;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import org.apache.commons.lang.math.RandomUtils;
import org.mobylet.core.Carrier;
import org.mobylet.core.Mobylet;
import org.mobylet.core.MobyletFactory;
import org.mobylet.core.device.Device;
import org.mobylet.core.device.DeviceDisplay;
import org.mobylet.core.gps.Gps;
import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.controller.validator.Validators;
import org.slim3.datastore.Datastore;
import org.slim3.util.StringUtil;
import org.slim3.util.TimeZoneLocator;

import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.datastore.Key;
import com.nakamachizu.collection.ApiKeyMap;
import com.nakamachizu.collection.IconNameMap;
import com.nakamachizu.collection.LineColorMap;
import com.nakamachizu.constant.AppConstant;
import com.nakamachizu.constant.MixiConstant;
import com.nakamachizu.constant.SessionKeyConstant;
import com.nakamachizu.model.LandMarkData;
import com.nakamachizu.model.LineData;
import com.nakamachizu.model.MapData;
import com.nakamachizu.model.MemberData;
import com.nakamachizu.model.session.MapDataSession;
import com.nakamachizu.service.MapDataService;
import com.nakamachizu.util.ErrorsUtil;
import com.nakamachizu.util.ForwardUtil;
import com.nakamachizu.util.ResourceUtil;

public class LocationController extends Controller {

    private static final Logger log = Logger.getLogger(LocationController.class.getName());
    
    protected List<String> messages = new ArrayList<String>();
    
    private MapDataService mds;
    private MapDataSession mdses;
    private MapData map;
    private Calendar now;
    private String pass;
    private String keyStr;
    private String zoomLevel;
    private String centerLat;
    private String centerLng;
    private String r;
    private String l;
    private String u;
    private String d;
    private String cmd;
    private String add;
    private String option;
    
    @Override
    public Navigation run() {
        
        if(!validate()){
            return forward(ForwardUtil.forwardJsp("location.jsp"));
        }
        
        this.init();

        if(StringUtil.isEmpty(zoomLevel)){
            zoomLevel = AppConstant.DEFAULT_ZOOM_LEVEL();
        }
        
        if(StringUtil.isEmpty(keyStr)){
            map = mds.getMapDataFromSession(mdses);
        }else{
            map = mds.getMapDataAccessKey(keyStr);
        }

        if(map == null){
            return forward(ForwardUtil.forwardJsp("nomap.jsp"));
        }
        
        if(!StringUtil.isEmpty(map.getViewPassword())){
            if(StringUtil.isEmpty(pass) && mdses != null){
                pass = mdses.getViewPassword();
            }
            if(!map.getViewPassword().equals(pass)){
                if(!StringUtil.isEmpty(pass)){
                    messages.add("パスワードが違います。");
                }
                return forward(ForwardUtil.forwardJsp("input_password.jsp"));
            }
        }
        

        Integer w = 600;
        Integer h = 370;
        
        Mobylet mobylet = MobyletFactory.getInstance();
        Carrier carrier = mobylet.getCarrier();
        if(!carrier.equals(Carrier.OTHER)){
            DeviceDisplay display = mobylet.getDisplay();
            Device device = mobylet.getDevice();

            w = display.getWidth();  //ブラウザ幅
            h = display.getHeight(); //ブラウザ高さ
            if(w == null || w.intValue() <= 0){
                w = 100;
            }
            if(h == null || h.intValue() <= 0){
                h = 100;
            }
            h = (int)(h * 0.7);
            

            Gps location = mobylet.getGps();
            String guid = mobylet.getGuid();
            if(location != null){
                DecimalFormat fmt = new DecimalFormat("###.0000000");
                String fmtLat = fmt.format(location.getLat());
                String fmtLng = fmt.format(location.getLon());
                centerLat = fmtLat;
                centerLng = fmtLng;
                messages.add("位置情報を取得しました。青いマーカーがあなたの現在位置です。この位置でよろしければ確定ボタンを押してください。");
            }
            
            if(!StringUtil.isEmpty(cmd) &&  !StringUtil.isEmpty(guid)){
                List<MemberData> memList = map.getMemberDataListRef().getModelList();
                List<LandMarkData> landList = map.getLandMarkDataListRef().getModelList();
                GeoPt geo = new GeoPt(Float.parseFloat(centerLat), Float.parseFloat(centerLng));
                
                // ランドマークを立てる場合
                if("land".equals(option)){
                    if(landList == null || landList.size() < AppConstant.MAX_LANDMARK_NUM()){
                        LandMarkData lmd = new LandMarkData();
                        lmd.setName("未入力");
                        lmd.setComment("未入力");
                        lmd.setLatLng(geo);
                        lmd.setUid(guid);
                        lmd.setPcIconName("landmark/text");
                        lmd.setMIconName(mds.createMobileLandmarkIconName(landList));
                        lmd.setEntryDate(now.getTime());
                        lmd.setModifyDate(now.getTime());
                        lmd.getMapDataRef().setModel(map);
                        lmd.setEditable(true);
                        Calendar expire = Calendar.getInstance(TimeZoneLocator.get());
                        expire.set(2050, 12, 31);
                        lmd.setExpireDate(expire.getTime());
                        
                        if(map.getAdminMailAddress().matches("^[0-9]+" + MixiConstant.MIXI_MAIL_ADDRESS() + "$")){
                            Calendar exp = Calendar.getInstance(TimeZoneLocator.get());
                            exp.add(Calendar.MINUTE, MixiConstant.MIXI_GPS_EXPIRE_MINUTES());
                            lmd.setExpireDate(exp.getTime());
                        }
                    
                        lmd.setKey(Datastore.put(lmd));
                        messages.add("ランドマークを追加しました。名前とコメントが未入力になっていますので編集リンクより入力してください。");
                        landList.add(lmd);
                    }else{
                        ErrorsUtil.putErrors(errors, request, "message.error.maxlandmark", "max_landmark");
                    }
                    
                // ランドマークを立てない場合
                }else{

                    MemberData mem = mds.getMemberDataUid(memList, guid);
                    if(mem == null){
                        if(memList.size() >= AppConstant.MAX_MEMBER_NUM()){
                            ResourceBundle bundle = ResourceUtil.getResourceBundle(request);
                            ErrorsUtil.putErrors(errors, bundle, "message.error.maxmember", "maxmember");
                        }else{
                            mem = new MemberData();
                            mem.setComment(AppConstant.DEFAULT_COMMENT());
                            String deviceName = null;
                            if(device != null){
                                deviceName = device.getDeviceProfile().get("機種名");
                            }
                            if(StringUtil.isEmpty(deviceName)){
                                deviceName = AppConstant.DEFAULT_NAME();
                            }
                            mem.setName(deviceName);
                            mem.setLatLng(geo);
                            mem.setUid(guid);
                            mem.setPcIconName(mds.createMemberPcIconName(memList));
                            mem.setMIconName(mds.createMobileMemberIconName(memList));
                            mem.setEntryDate(now.getTime());
                            mem.setModifyDate(now.getTime());
                            mem.getMapDataRef().setModel(map);
                            mem.setEditable(true);
                            Calendar expire = Calendar.getInstance(TimeZoneLocator.get());
                            expire.set(2050, 12, 31);
                            mem.setExpireDate(expire.getTime());
                            String label = IconNameMap.MOBILE_MEMBER_ICON_NAME.get(Integer.toString(memList.size()));
                            messages.add("あなたの現在のラベルは「" + label.toUpperCase() + "」です。ラベルは参加者の増減で変わります。参加者一覧のあなたのラベルの編集ボタンを押して名前やコメントを変更してください。");
                            memList.add(mem);
                        }
                    }else{
                        if("line".equals(option)){
                           List<LineData>llist = map.getLineDataListRef().getModelList();
                           if(llist == null || llist.size() <= 100){
                               String col = mds.getLineColor(map, guid);
                                if(StringUtil.isEmpty(col)){
                                    col = LineColorMap.LINE_COLOR_MAP.get(Integer.toString(RandomUtils.nextInt(15) + 1));
                                }
                                LineData ldata = new LineData();
                                ldata.setColor(col);
                                ldata.setStart(mem.getLatLng());
                                ldata.setGuid(guid);
                                ldata.setEnd(geo);
                                ldata.setEntryDate(now.getTime());
                                ldata.setModifyDate(now.getTime());
                                ldata.getMapDataRef().setModel(map);
                                Calendar expire = Calendar.getInstance(TimeZoneLocator.get());
                                expire.set(2050, 12, 31);
                                ldata.setExpireDate(expire.getTime());

                                if(map.getAdminMailAddress().matches("^[0-9]+" + MixiConstant.MIXI_MAIL_ADDRESS() + "$")){
                                    Calendar exp = Calendar.getInstance(TimeZoneLocator.get());
                                    exp.add(Calendar.MINUTE, MixiConstant.MIXI_GPS_EXPIRE_MINUTES());
                                    ldata.setExpireDate(exp.getTime());
                                }
                                Datastore.put(ldata);
                           }
                        }
                        
                        messages.add("位置情報を更新しました。");
                        mem.setLatLng(geo);
                        mem.setModifyDate(now.getTime());
                        mem.setEditable(true);
                        
                    }
                    if(mem != null){
                        if(map.getAdminMailAddress().matches("^[0-9]+" + MixiConstant.MIXI_MAIL_ADDRESS() + "$")){
                            Calendar exp = Calendar.getInstance(TimeZoneLocator.get());
                            exp.add(Calendar.MINUTE, MixiConstant.MIXI_GPS_EXPIRE_MINUTES());
                            mem.setExpireDate(exp.getTime());
                        }
                        mem.setKey(Datastore.put(mem));

                        //mem.setKeyStr(KeyFactory.keyToString(mem.getKey()));
                    }
                }
                requestScope("landMarkList", landList);
                requestScope("memberList", memList);
                float angle = mds.getAngle(zoomLevel, w);
                requestScope("now", now.getTime().getTime());
                requestScope("angle", angle);
                requestScope("serverName", request.getServerName());
                requestScope("zoom", zoomLevel);
                requestScope("messages", messages);
                requestScope("apiKey", ApiKeyMap.API_KEY_MAP.get(request.getServerName()));
                requestScope("width", w);
                requestScope("height", h);
                requestScope("expire", map.getExpire());
                mdses = mds.createMapDataSession(map);
                mdses.setViewPassword(pass);
                sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY(), mdses);
                return forward(ForwardUtil.forwardJsp("map.jsp"));
            }

        }else{
            requestScope("iconNameMap", IconNameMap.ICON_NAME);
        }
        
        if(!StringUtil.isEmpty(centerLat) && !StringUtil.isEmpty(centerLng)){
            GeoPt center = mds.getCenter(centerLat, centerLng, Float.parseFloat(r), Float.parseFloat(l), Float.parseFloat(u), Float.parseFloat(d));
            centerLat = Float.toString(center.getLatitude());
            centerLng = Float.toString(center.getLongitude());
            requestScope("cLat", centerLat);
            requestScope("cLng", centerLng);
        }else{
            centerLat = "35.6810976";
            centerLng = "139.7671169";
            requestScope("cLat", centerLat);
            requestScope("cLng", centerLng);
        }
        
        float angle = mds.getAngle(zoomLevel, w) / 2.0f;
        requestScope("now", now.getTime().getTime());
        requestScope("angle", angle);
        requestScope("serverName", request.getServerName());
        requestScope("zoom", zoomLevel);
        requestScope("messages", messages);
        requestScope("apiKey", ApiKeyMap.API_KEY_MAP.get(request.getServerName()));
        requestScope("width", w);
        requestScope("height", h);
        requestScope("expire", map.getExpire());
        mdses = mds.createMapDataSession(map);
        mdses.setViewPassword(pass);
        sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY(), mdses);
        add = centerLat + "," + centerLng;
        requestScope("add", add);
        return forward(ForwardUtil.forwardJsp("location.jsp"));
    }
    
    
    
    protected void init(){
        
        mds = new MapDataService();
        mdses = sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY());
        map = null;
        now = Calendar.getInstance(TimeZoneLocator.get());
        
        cmd = requestScope("cmd");
        pass = requestScope("viewPassword");
        keyStr = requestScope("key");
        zoomLevel = requestScope("zoom");
        centerLat = requestScope("cLat");
        centerLng = requestScope("cLng");
        option = requestScope("option");
        r = requestScope("r");
        l = requestScope("l");
        u = requestScope("u");
        d = requestScope("d");
        
        if(StringUtil.isEmpty(r)){
            r = "0";
        }
        if(StringUtil.isEmpty(l)){
            l = "0";
        }
        if(StringUtil.isEmpty(u)){
            u = "0";
        }
        if(StringUtil.isEmpty(d)){
            d = "0";
        }
    }
    
    @Override
    protected Navigation handleError(Throwable error) {
        error.printStackTrace();
        log.severe(ErrorsUtil.stackTrace(error));
        return forward("error.jsp");
    }
    
    protected boolean validate() {
        boolean result = false;
        Validators v = new Validators(request);
        String reqKey = requestScope("key");
        if(StringUtil.isEmpty(reqKey)){
            MapDataSession ses = sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY());
            if(ses != null){
                requestScope("key", ses.getMapKey());
            }
        }
        v.add("key", v.required());
        v.add("zoom", v.longRange(0, 19));
        v.add("cLat", v.floatType());
        v.add("cLng", v.floatType());
        v.add("cLat", v.doubleRange(-90.0, 90.0));
        v.add("cLng", v.doubleRange(-180.0, 180.0));
        v.add("r", v.floatType());
        v.add("l", v.floatType());
        v.add("u", v.floatType());
        v.add("d", v.floatType());
        v.add("r", v.doubleRange(0.0001, 1.0));
        v.add("l", v.doubleRange(0.0001, 1.0));
        v.add("u", v.doubleRange(0.0001, 1.0));
        v.add("d", v.doubleRange(0.0001, 1.0));
        
        result = v.validate();
        
        return result;
    }
}
